package com.wilschrader.demarche;

import java.util.ArrayList;
import java.util.EnumSet;

/**
 * Anything that can contain units will extend this.
 * Damage escalates downward in that if this unit is destroyed then all units under it are destroyed.
 * Likewise, resetting this unit resets all units under it.
 * 
 * @author Wil Schrader
 * @modifications
 * 	08172011	Init revision.
 * 
 */
public class UnitContainer {
	
	protected ArrayList<Unit> activeUnits = new ArrayList<Unit>();
	protected ArrayList<Unit> destroyedUnits = new ArrayList<Unit>();
	
	/**
	 * Units that are considered part of a fleet are added here.
	 * 
	 * @param unit
	 */
	public void addUnit(Unit unit) {
		Simulation.debugOutput("Adding Unit (" + unit.getClass().getSimpleName() + ").");
		activeUnits.add(unit);
	}
	
	/**
	 * For many units that are identical, use this shortcut to add numerous copies.
	 * 
	 * @param unit		The unit to add.
	 * @param copies	The number of copies that should be added.
	 */
	public void addUnits(Unit unit, int copies) {
		Simulation.debugOutput("Adding " + copies + " Units (" + unit.getClass().getSimpleName() + ").");
		for (int i = 0; i < copies; i++) {
			try {
				Unit copiedUnit = unit.clone();
				activeUnits.add(copiedUnit);
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Resets the fleet and all units.
	 */
	public void reset() {
		activeUnits.clear();
		activeUnits.addAll(destroyedUnits);
		destroyedUnits.clear();
		
		for (Unit unit: activeUnits) {
			unit.reset();
		}
	}
	
	/**
	 * Retrieve a list of all active units.
	 * 
	 * @return
	 */
	public ArrayList<Unit> getAllActiveUnits() {
		return activeUnits;
	}
	
	/**
	 * Returns all units that either sustain damage or not
	 * 
	 * @param sustainDamage
	 * @return
	 */
	public ArrayList<Unit> getActiveUnits(boolean sustainDamage) {
		ArrayList<Unit> returnUnits = new ArrayList<Unit>();
		
		for (Unit unit: activeUnits) {
			if (unit.sustainsDamage() == sustainDamage)
				returnUnits.add(unit);
		}
		
		return returnUnits;
	}
	
	/**
	 * Returns all units that either sustain damage or not, that are damaged or not.
	 * 
	 * @param sustainDamage
	 * @param damaged
	 * @return
	 */
	public ArrayList<Unit> getActiveUnits(boolean sustainDamage, boolean damaged) {
		ArrayList<Unit> returnUnits = new ArrayList<Unit>();
		
		for (Unit unit: activeUnits) {
			if (unit.sustainsDamage() == sustainDamage && unit.isDamaged() == damaged)
				returnUnits.add(unit);
		}
		
		return returnUnits;
	}
	
	/**
	 * Retrieve a list of all units of a specific type.
	 * 
	 * @param type
	 * @return
	 */
	public ArrayList<Unit> getActiveUnits(Unit.Type type) {
		ArrayList<Unit> returnUnits = new ArrayList<Unit>();
		
		for (Unit unit: activeUnits) {
			if (unit.getType() == type)
				returnUnits.add(unit);
		}
		
		return returnUnits;
	}
	
	/**
	 * Retrieve a list of all units of a list of specific types.
	 * 
	 * @param types
	 * @return
	 */
	public ArrayList<Unit> getActiveUnits(EnumSet<Unit.Type> types) {
		ArrayList<Unit> returnUnits = new ArrayList<Unit>();
		
		for (Unit unit: activeUnits) {
			if (types.contains(unit.getType()))
				returnUnits.add(unit);
		}
		
		return returnUnits;
	}
	
	/**
	 * Destroys a unit by removing it from the active unit list.
	 * 
	 * @param unit
	 */
	public void destroyUnit(Unit unit) {
		destroyedUnits.add(unit);
		activeUnits.remove(unit);
		
		if (unit.count() > 0) {
			unit.destroyAllUnits();
		}
	}
	
	public void destroyAllUnits() {
		for (Unit unit : activeUnits) {
			destroyedUnits.add(unit);
		}
		
		activeUnits.clear();
	}
	
	/**
	 * Will count the number of units of type are on the unit.
	 * 
	 * @param types	The types of units.
	 * @return		The total number of units of that type on board this unit.
	 */
	public int count(EnumSet<Unit.Type> types, boolean active, boolean destroyed) {
		int count = 0;
		
		if (active) {
			for (Unit unit : activeUnits) {
				if (types.contains(unit.getType()))
					count++;
			}
		}
		
		if (destroyed) {
			for (Unit unit : destroyedUnits) {
				if (types.contains(unit.getType()))
					count++;
			}
		}
		
		return count;
	}
	
	public int count(EnumSet<Unit.Type> types) {
		return count(types, true, false);
	}
	
	public int count(Unit.Type type) {
		return count(EnumSet.of(type), true, false);
	}
	
	public int count(){
		return count(EnumSet.allOf(Unit.Type.class), true, false);
	}
}
